using System;
using System.Collections.Generic;
using System.Text;
using System.ComponentModel;
using CodePlex.SpaceBlock;

namespace CodePlex.SpaceBlock.UI
{

    public interface IMetadataSuggestions
    {
        bool SupportsSuggestions(string key);
        IEnumerable<string> GetSuggestions(string key);

    }

    public class MetadataDescriptor : ICustomTypeDescriptor
    {
        private readonly Metadata _metadata;
        private readonly MetadataTypeConverter _converter;
        private readonly IMetadataSuggestions _suggestions;
        
        public MetadataDescriptor(Metadata metadata, IMetadataSuggestions suggestions)
        {
            _metadata = metadata;
            _suggestions = suggestions ?? new NoSuggestions();
            _converter = new MetadataTypeConverter(this);
        }

        public IMetadataSuggestions Suggestions
        {
            get
            {
                return _suggestions;
            }
        }

        public Metadata Metadata
        {
            get
            {
                return _metadata;
            }
        }

        public object GetPropertyOwner(PropertyDescriptor pd)
        {
            return _metadata;
        }

        public AttributeCollection GetAttributes()
        {
            AttributeCollection rt = new AttributeCollection();
            return rt;
        }

        public object GetEditor(Type editorBaseType)
        {
            return null;
        }

        public string GetClassName()
        {
            return _metadata.GetType().FullName;
        }

        public TypeConverter GetConverter()
        {
            return _converter;
        }

        public PropertyDescriptorCollection GetProperties(Attribute[] attributes)
        {
            return null;
        }

        public PropertyDescriptor GetDefaultProperty()
        {
            return null;
        }

        public EventDescriptorCollection GetEvents(Attribute[] attributes)
        {
            return null;
        }

        public string GetComponentName()
        {
            throw new NotImplementedException();
        }

        public EventDescriptor GetDefaultEvent()
        {
            throw new NotImplementedException();
        }

        public EventDescriptorCollection GetEvents()
        {
            throw new NotImplementedException();
        }

        public PropertyDescriptorCollection GetProperties()
        {
            throw new NotImplementedException();
        }




        private class NoSuggestions : IMetadataSuggestions
        {
            public bool SupportsSuggestions(string key)
            {
                return false;
            }

            public IEnumerable<string> GetSuggestions(string key)
            {
                yield break;
            }
        }


        private class MetadataTypeConverter : TypeConverter
        {

            private readonly MetadataDescriptor _parent;
 
            public MetadataTypeConverter(MetadataDescriptor parent)
            {
                _parent = parent;
            }

            public MetadataDescriptor MetadataDescriptor { get { return _parent; } }

            public override bool GetPropertiesSupported(ITypeDescriptorContext context)
            {
                return true;
            }

            public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object value, Attribute[] attributes)
            {
                List<PropertyDescriptor> props = new List<PropertyDescriptor>();

                foreach (string key in _parent.Metadata.Keys)
                {
                    MetadataPropertyDescriptor mpd;

                    if (this.MetadataDescriptor.Suggestions.SupportsSuggestions(key))
                    {
                        mpd = new MetadataPropertyDescriptor(
                            this,
                            key,
                            _parent.Metadata.GetCategory(key),
                            _parent.Metadata.GetDescription(key),
                            typeof(ListToPropertyConverter)
                            );

                       
                    }
                    else
                    {
                        mpd = new MetadataPropertyDescriptor(
                            this,
                            key,
                            _parent.Metadata.GetCategory(key),
                            _parent.Metadata.GetDescription(key)
                            );
                    }
                    props.Add(mpd);


                }
                return new PropertyDescriptorCollection(props.ToArray());
            }

            // http://gaaton.blogspot.com/2007/11/how-to-add-combobox-to-propertygrid.html
            private class ListToPropertyConverter : StringConverter
            {
                public override bool GetStandardValuesSupported(ITypeDescriptorContext context)
                {
                    //True - means show a Combobox
                    //and False for show a Modal 
                    return true;
                }

                public override bool GetStandardValuesExclusive(ITypeDescriptorContext context)
                {
                    //False - a option to edit values 
                    //and True - set values to state readonly
                    return false;
                }

                public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext context)
                {
                    MetadataDescriptor md = context.Instance as MetadataDescriptor;

                    return new StandardValuesCollection(new List<string>(md.Suggestions.GetSuggestions(context.PropertyDescriptor.Name)));
                }
            }

       

            private class MetadataPropertyDescriptor : PropertyDescriptor
            {
                private readonly MetadataTypeConverter _parent;

              


                public MetadataPropertyDescriptor(MetadataTypeConverter parent, string name, string category, string description, Type typeConverterType)
                    : base(name, new Attribute[] { 
                        new CategoryAttribute(category),
                        new DescriptionAttribute(description),
                        new TypeConverterAttribute(typeConverterType)})
                {
                    _parent = parent;
                }


                public MetadataPropertyDescriptor(MetadataTypeConverter parent, string name, string category, string description)
                    : base(name, new Attribute[] { 
                        new CategoryAttribute(category),
                        new DescriptionAttribute(description)})
                {
                    _parent = parent;
                }

                public override object GetValue(object component)
                {
                    Metadata metadata = component as Metadata;
                    return metadata[this.Name];
                }

                public override bool IsReadOnly
                {
                    get 
                    {
                        return _parent._parent.Metadata.GetReadOnly(this.Name);
                    }
                }

                public override Type PropertyType
                {
                    get { return typeof(string); }
                }

                public override bool ShouldSerializeValue(object component)
                {
                    return false;
                }

                public override void SetValue(object component, object value)
                {
                    Metadata metadata = component as Metadata;
                    metadata[this.Name] = value as string;
                }

                public override bool CanResetValue(object component)
                {
                    throw new NotImplementedException();
                }

                public override Type ComponentType
                {
                    get
                    {
                        return typeof(Metadata);
                    }
                }
              
                public override void ResetValue(object component)
                {
                    throw new NotImplementedException();
                }


            }
         
        }
    }
}
